package com.jgraph.editor.persistence;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import com.jgraph.editor.JGraphEditorSettings;

/**
 * This singleton class is a factory for {@link JGraphPersistenceManager}s.
 * Persistence managers are created as needed by
 * {@link JGraphPersistenceManagerFactoryMethod}s registered in this factory
 * and cached for reuse. Each persistence manager handles specific file types
 * (identified by their extension) which are mapped to its factory method.
 * This enables users to request persistence manager for files of a given type.
 * If a persistence manager for such a type is cached it is returned, otherwise,
 * if a factory method for creating the manager exists the latter is created,
 * cached, and returned. 
 *
 * Usage example: <code>
 * 	JGraphPersistenceManager<?> manager = 
 *			JGraphPersistenceManagerFactory.getInstance()
 *			.getPersistenceManager("xml.gz");
 * </code>
 * @author svetoslavganov@mail.utexas.edu
 *
 */
public final class JGraphPersistenceManagerFactory {

	private static JGraphPersistenceManagerFactory sInstance;
	
	private static JGraphEditorSettings sSettings;

	/**
	 * Returns the singleton instance of this class.
	 *
	 * @return The singleton instance.
	 */
	public static JGraphPersistenceManagerFactory getInstance() {
		if (sInstance == null) {
			sInstance = new JGraphPersistenceManagerFactory();
		}
		return sInstance;
	}

	private Map<String, JGraphPersistenceManagerFactoryMethod<?>> mFactoryMethodsMap =
	    new HashMap<String, JGraphPersistenceManagerFactoryMethod<?>>();

	private Map<String, JGraphPersistenceManager<?>> mPersistenceManagersMap =
		new HashMap<String, JGraphPersistenceManager<?>>();

	/*
	 * Hide the constructor. 
	 */
	private JGraphPersistenceManagerFactory() {
		/* do nothing */
	}

	/**
	 * Returns a {@link JGraphPersistenceManager} given a file {@link URI}.
	 * The URI is parsed and the file extension (defined as the substring of
	 * the URI from the first '.' to the end of the path segment of the URI)
	 * is extracted.
	 *
	 * @param uri The file URI.
	 * @return A persistence manager instance or null if no factory method
	 *         for manager that handles the given file type (i.e. extension)
	 *         exists.
	 */
	@SuppressWarnings("unchecked")
	public <T> JGraphPersistenceManager<T> getPersistenceManager(
			String uri) {
		String extension = getFileExtension(uri);
        JGraphPersistenceManager<?> manager =
        	mPersistenceManagersMap.get(extension);
        if (manager == null) {
        	JGraphPersistenceManagerFactoryMethod<?> factoryMethod =
        		mFactoryMethodsMap.get(extension);
        	if (factoryMethod != null) {
        		manager = factoryMethod.createPersistenceManager(
        				factoryMethod.getProperties());
        		mPersistenceManagersMap.put(extension, manager);
        	}
        }
		return (JGraphPersistenceManager<T>) manager;
	}

	/**
	 * Returns the extension of a file given its {@link URI}.
	 *
	 * @param uri The URI.
	 * @return The file extension or null if such does not exists.
	 */
    public static String getFileExtension(String uri) {
    	try {
    	    String fileName = new URI(uri.replace('\\', '/')).getPath();
    	    int fromIndex = fileName.indexOf('.') + 1;
    	    if (fromIndex > 0) {
    	    	int toIndex = fileName.length();
    	    	return fileName.substring(fromIndex, toIndex).toLowerCase();
    	    }
    	} catch (URISyntaxException use) {
    		/* ignore */
    	}
    	return null;
    }

	/**
	 * Adds a {@link JGraphPersistenceManagerFactoryMethod} to this factory.
	 *
	 * @param factoryMethod The factory method to add.
	 */
    public void addFactoryMethod(
		    JGraphPersistenceManagerFactoryMethod<?> factoryMethod) {
	    for (String fileExtension : factoryMethod.getFileExtensions()) {
	    	mFactoryMethodsMap.put(fileExtension.toLowerCase(), factoryMethod);
	    }
    }
}
